﻿using spaar.ModLoader;
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;


namespace NoBounds
{

    public class NoBoundsSpawner : spaar.ModLoader.Mod
    {

        public override string Author
        {
            get { return "ITR"; }
        }

        public override string DisplayName
        {
            get { return "NoBounds"; }
        }

        public override string Name
        {
            get { return "NoBoundsMod"; }
        }
        NoBoundsMod noBoundsMod;
        public override void OnLoad()
        {
            noBoundsMod = NoBoundsMod.Instance;
            Game.DontDestroyOnLoad(noBoundsMod.gameObject);
        }

        public override void OnUnload()
        {

            if (noBoundsMod != null)
            {
                Game.Destroy(noBoundsMod.gameObject);
            }
        }

        public override bool CanBeUnloaded
        {
            get
            {
                return true;
            }
        }

        public override Version Version
        {
            get { return new Version(1, 0); }
        }
    }

    public class NoBoundsMod : spaar.ModLoader.SingleInstance<NoBoundsMod>
    {
        MultiKeyBlockBehaviour multiKeyBlockBehaviour;
        MyBlockInfo myBlockInfo;

        public void Start()
        {
            Refresh = -1;
            myBlockInfo = gameObject.AddComponent<MyBlockInfo>();

            spaar.ModLoader.Commands.RegisterHelpMessage("NoBounds commands:\n	none");

            spaar.ModLoader.Commands.RegisterCommand("none", (args, namedArgs) => {
                int oldType = spaar.ModLoader.Game.AddPiece.BlockType;
                spaar.ModLoader.Game.AddPiece.SetBlockType(23);
                AddPiece addPiece = spaar.ModLoader.Game.AddPiece;
                MyBlockInfo[] myBlockInfos = FindObjectsOfType<MyBlockInfo>();
                for (int i = 0; i < myBlockInfos.Length; i++)
                {
                    if (myBlockInfos[i].GetComponent<Rigidbody>() != null)
                    {
                        myBlockInfos[i].name = "TEMP" + myBlockInfos[i].name;
                        addPiece.AddBlock(myBlockInfos[i].transform);
                    }
                }
                myBlockInfos = FindObjectsOfType<MyBlockInfo>();
                for (int i = 0; i < myBlockInfos.Length; i++)
                {
                    if (myBlockInfos[i].GetComponent<Rigidbody>() != null)
                    {
                        if (myBlockInfos[i].name.Contains("TEMP"))
                        {
                            myBlockInfos[i].name = myBlockInfos[i].name.Substring(4);
                        }
                        else
                        {
                            myBlockInfos[i].transform.localScale /= 10;
                        }
                    }
                }
                spaar.ModLoader.Game.AddPiece.SetBlockType(oldType);
                return "NoBounds doesn't have any commands...";
            }, "NoBounds doesn't have any commands...");
        }

        private BlockMapper blockMapper;
        BlockBehaviour block;

        public int Refresh;
        private int openMultiKeyMapperIfNotOpen = -1;
        public void Update()
        {
            if (Game.IsSimulating)
                return;
            if (openMultiKeyMapperIfNotOpen > 0)
            {
                if (--openMultiKeyMapperIfNotOpen == 0)
                {
                    if (blockMapper == null)
                    {
                        blockMapper = BlockMapper.Open(multiKeyBlockBehaviour);
                        block = multiKeyBlockBehaviour;
                    }
                }
            }
            if (blockMapper == null)
            {
                blockMapper = BlockMapper.CurrentInstance;
                block = null;
            }
            else
            {
                if (blockMapper.Block != block)
                {
                    block = blockMapper.Block;
                    if (block != null && !(block is MultiKeyBlockBehaviour))
                    {
                        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
                        {
                            OpenMultiKeyMapper(block, 0);
                            return;
                        }

                        block.MapperTypes.ForEach((mapperType) => {
                            mapperType.DisplayStateChanged += (state) => {
                                Refresh = 2;
                            };
                        });
                        ContainerDetails[] containers = blockMapper.GetComponentsInChildren<ContainerDetails>();
                        for (int i = 0; i < containers.Length; i++)
                        {
                            ModifySliderSelector(containers[i].transform);
                        }
                    }
                }
                else if (Refresh > 0)
                {
                    if (--Refresh == 0)
                    {
                        ContainerDetails[] containers = blockMapper.GetComponentsInChildren<ContainerDetails>();
                        for (int i = 0; i < containers.Length; i++)
                        {
                            ModifySliderSelector(containers[i].transform);
                        }
                    }
                }
            }
        }

        public static void ModifySliderSelector(Transform transform)
        {
            Selectors.SliderSelector sliderSelector = transform.GetComponentInChildren<Selectors.SliderSelector>();
            Selectors.ValueHolder valueHolder = transform.GetComponentInChildren<Selectors.ValueHolder>();
            if (sliderSelector && valueHolder)
            {
                ModifySliderSelector(sliderSelector, valueHolder);
            }
        }

        public static void ModifySliderSelector(Selectors.SliderSelector sliderSelector, Selectors.ValueHolder valueHolder)
        {
            valueHolder.CharLimit = int.MaxValue;
            valueHolder.ValueChanged += (value) => { sliderSelector.Value = value; };
        }

        public void OpenMultiKeyMapper(BlockBehaviour blockBehaviour, int currentKey)
        {
            if (multiKeyBlockBehaviour != null) Destroy(multiKeyBlockBehaviour);
            myBlockInfo.blockName = blockBehaviour.name + " (MultiKeyMap)";
            openMultiKeyMapperIfNotOpen = 2;

            multiKeyBlockBehaviour = gameObject.AddComponent<MultiKeyBlockBehaviour>();
            multiKeyBlockBehaviour.enabled = false;
            multiKeyBlockBehaviour.block = blockBehaviour;
            multiKeyBlockBehaviour.currentKey = currentKey;
            multiKeyBlockBehaviour.enabled = true;

            this.block = multiKeyBlockBehaviour;
            blockMapper = BlockMapper.Open(multiKeyBlockBehaviour);
        }

        public override string Name
        {
            get { return "NoBoundsGameObject"; }
        }
    }

    public class MultiKeyBlockBehaviour : BlockBehaviour
    {
        public BlockBehaviour block;
        public int currentKey;
        private int updateKeys;
        private MMenu mMenu;

        private List<MKeyAndDelegate> mkeys;
        private struct MKeyAndDelegate
        {
            public MKey mKey;
            public MKey old;
            public int spot;
            public bool replace;
            public MKeyAndDelegate(MKey mKey, MKey old, int spot, bool replace)
            {
                this.mKey = mKey;
                this.old = old;
                this.spot = spot;
                this.replace = replace;
            }
        }


        public void OnEnable()
        {
            if (block == null) return;
            updateKeys = 5;
            mkeys = new List<MKeyAndDelegate>();

            List<MKey> keys = block.Keys;
            if (currentKey < 0) currentKey = 0;
            currentKey = currentKey % keys.Count;

            MKey key = keys[currentKey];

            List<KeyCode> keyCodes = key.KeyCode;
            int i = 0;
            foreach (KeyCode keyCode in keyCodes)
            {
                mkeys.Add(new MKeyAndDelegate(AddKey(key.DisplayName + " #" + i, key.Key + "#" + i, keyCode), key, i, false));
                i++;
            }
            mkeys.Add(new MKeyAndDelegate(AddKey(key.DisplayName + " #" + i, key.Key + "#" + i, KeyCode.None), key, i, true));

            if (keys.Count > 1)
            {
                mMenu = AddMenu("Key", 1, new List<string>(new String[] { "", key.DisplayName, "" }));
                mMenu.ValueChanged += (j) => {
                    NoBoundsMod.Instance.OpenMultiKeyMapper(block, (currentKey + j + keys.Count - 1) % keys.Count);
                };
            }

            StoreInitialData();
        }

        public void Update()
        {
            if (updateKeys > 0)
            {
                if (--updateKeys == 0)
                {
                    if (block == null) return;

                    foreach (MKeyAndDelegate mKey in mkeys)
                    {
                        MKey.KeyboardKeyPressed keyboardPressed = mKey.mKey.UpdateKey != null ? (MKey.KeyboardKeyPressed)mKey.mKey.UpdateKey.Clone() : null;
                        mKey.mKey.UpdateKey = () => {
                            mKey.old.AddOrReplaceKey(mKey.spot, mKey.mKey.KeyCode[0]);
                            if (mKey.replace || keyboardPressed == null)
                            {
                                NoBoundsMod.Instance.OpenMultiKeyMapper(block, currentKey);
                            }
                            else
                            {
                                keyboardPressed();
                            }
                        };
                    }
                }
            }
        }

        public override int GetBlockID()
        {
            return -1;
        }

        public override void OnReset()
        {
            FieldInfo[] fields = typeof(MKey).GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
            for (int i = 0; i < fields.Length; i++)
            {
                if (fields[i].Name == "_keyCodes")
                {
                    KeyCode[] none = new KeyCode[] { KeyCode.None };
                    foreach (MKey key in block.Keys)
                    {
                        fields[i].SetValue(key, new List<KeyCode>(none));
                    }
                    break;
                }
            }
            NoBoundsMod.Instance.OpenMultiKeyMapper(block, currentKey);
        }

        public override void OnLoad(XDataHolder data)
        {

        }

        public override void OnSave(XDataHolder data)
        {

        }
    }
}